/* tripl/arch/x86_64/x86_64_math.c 
 * 
 * This file is part of tripl. 
 * 
 * tripl is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * tripl is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with tripl. If not, see <https://www.gnu.org/licenses/>
 */ 




/* parse a math-expression, with no prio
 * i'll write down a '****' when i feel angry */

#include <stdio.h>
#include <string.h>

#include <tripl/tripl.h>
#include <tripl/arch.h>
#include <tripl/limits.h>

#include <klist.h>



extern char *psave;
extern int lno;

extern struct klist_node *find_name(char *);


__ll_asm const static char do_op_nr[] = {
	"	%sq 	$%d, %%r12\n"
};

__ll_asm const static char do_op_glbvar[] = {
	"	%sq 	%s(%%rip), %%r12\n"
};

__ll_asm const static char do_op_stkvar[] = {
	"	%sq 	-%ld(%%rbp), %%r12\n"
};

__ll_asm const static char store_res_to_stkvar[] = {
	"	movq 	%%r12, -%ld(%%rbp)\n"
};

__ll_asm const static char store_res_to_glbvar[] = {
	"	movq 	%%r12, %s(%%rip)\n"
};

__ll_asm const static char store_r12_directly[] = {
	"	movq 	$%ld, %%r12\n"
};


static int store_res(char *name) {
	struct klist_node *n = find_name(name);

	if (unlikely(!n)) 
		exit(generic_error("name not found", psave, lno));

	if (unlikely(n->xmask & 4)) 
		exit(generic_error("variable sets to be 'read-only'", psave, lno));

	if (n->xmask & 1) 
		printf(store_res_to_glbvar, name);

	else 
		printf(store_res_to_stkvar, n->value << 3);

	return 0;
}

static int operation_var(char *opr, char *name) {
	struct klist_node *n = find_name(name);

	if (unlikely(!n)) 
		exit(generic_error("name not found", psave, lno));

	if (n->xmask & 1) 
		printf(do_op_glbvar, opr, name);

	else 
		printf(do_op_stkvar, opr, n->value << 3);

	return 0;
}


#define operation_nr(opr, nr) 	printf(do_op_nr, opr, nr)


 int x86_64_math_expr(char *p, char *var) {
	int type, out;
	int op, i;
	static char *operations[] = {"add", "sub"};
	char *opstr;

	p++;
	_skip_spaces(p);

	/* no any prio supported */

	for (i = 0; getword(p, &type, &out); i++) {
		if (unlikely(!i)) {
			/* the first number is the initialize value of r12 */

			printf(store_r12_directly, (long) out);
			_skip_word(p);
			_skip_spaces(p);

			continue;
		}

		/* operator */
		op = out;

		switch (op) {
		case '+':
			opstr = operations[0];
			
			break;

		case '-':
			opstr = operations[1];

			break;

		default:
			exit(generic_error("invalid operator", psave, lno));
		}

		p++;
		_skip_spaces(p);

		getword(p, &type, &out);

		/* number or name, add or sub */

		if (type == WORDTYPE_NR) {
			operation_nr(opstr, out);
	
			_skip_word(p);
		}
		
		else if (type == WORDTYPE_NAME) {
			char *name = strndup(p, out);

			operation_var(opstr, name);

			free(name);

			p += out;
		}

		else 
			exit(generic_error("invalid token in math expression", psave, lno));

		_skip_spaces(p);
	}

	store_res(var);

	return 0;
}
